home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Utilities / RemoteCommand / Source / execServer.subproj / ExecServer.m < prev    next >
Encoding:
Text File  |  1993-06-09  |  33.4 KB  |  1,211 lines

  1. // -------------------------------------------------------------------------------------
  2. // ExecServer
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9.  
  10. #import <appkit/appkit.h>
  11. #import <libc.h>
  12. #import <mach/mach.h>
  13. #import <mach/cthreads.h>
  14. #import <stdlib.h>
  15. #import <string.h>
  16. #import <c.h>
  17. #import <pwd.h>
  18. #import <sys/param.h>
  19. #import <sys/types.h>
  20. #import <sys/stat.h>
  21. #import <sys/time.h>
  22. #import <sys/dir.h>
  23. #import <mach/mach_traps.h>
  24. #import <remote/NXConnection.h>
  25. #import <remote/NXProxy.h>
  26. #import <machkit/NXPort.h>
  27. #import "ExecServer.h"
  28.  
  29. // -------------------------------------------------------------------------------------
  30. // These variables are set in the main process, then reset in the child server process
  31. uid_t                    exeUserUid;                    // login user uid
  32. uid_t                    exeRootUid;                    // root user uid
  33.  
  34. // -------------------------------------------------------------------------------------
  35. // Keep track of child process level. Used for debugging purposes only.
  36. int                        exeChildLevel = 0;
  37. #define    forkCHILD        ({extern int exeChildLevel;int c=fork();if(!c)exeChildLevel++;c;})
  38.  
  39. // -------------------------------------------------------------------------------------
  40. // local implementation of NXCopyStringBuffer()
  41. #define    STRCOPY(X)        strcpy((char*)malloc(strlen(X) + 1), (X));
  42.  
  43. // *************************************************************************************
  44. // *************************************************************************************
  45. // _ExecServer_d / RunCommand declarations
  46. // *************************************************************************************
  47. // *************************************************************************************
  48.  
  49. // -------------------------------------------------------------------------------------
  50. // ExecServer shared status structure
  51. typedef struct _shareStat_s {
  52.     int                    errCode;
  53.     char                errMsg[512];
  54.     char                execScript[MAXPATHLEN + 1];
  55. } shareStat_t;
  56.  
  57. // -------------------------------------------------------------------------------------
  58. // Root_process server protocols
  59.  
  60. @protocol RunService
  61. - (void)_pingServer;
  62. - (BOOL)_isRunningAsRoot;
  63. - (execHandle_t)_runCommand:(const char*)command
  64.         user:(const char*)userName:(const char*)password
  65.         client:(id <RemoteClient>)theClient
  66.         kill:(BOOL)killOnError;
  67. - (int)_uperform:(SEL)method withArg:(const char*)arg
  68.         user:(const char*)userName:(const char*)password;
  69. - (void)_terminateCommand:(execHandle_t)runId;
  70. - (void)_killCommand:(execHandle_t)runId;
  71. - (BOOL)_childIsActive:(execHandle_t)runId;
  72. - (void)_shutDownServer;
  73. @end
  74.  
  75. @protocol RootInternal        // sent by RunCommander to server
  76. - (void)_commandOutput:(const char*)buf len:(int)len execId:(u_int)fakeRun;
  77. - (void)_commandDidComplete:(u_int)fakeRun;
  78. @end
  79.  
  80. // -------------------------------------------------------------------------------------
  81. // RunCommand declaration
  82.  
  83. @interface RunCommand : Object <NXSenderIsInvalid>
  84. { @public
  85.     int                inputDescriptor;        // input to csh pipe
  86.     id                client;                    // object to send csh output
  87.     BOOL            killOnError;            // kill child process on 'senderIsInvalid:'
  88.     id                server;                    // _ExecServerd
  89.     int                cmdChild;                // child pid
  90.     int                exitErr;                // returned exit status
  91.     shareStat_t        *shareMem;                // common memory for error information
  92.     char            *cmdName;                // optional name
  93. }
  94. - (int)_pipeExec:(const char*)cmd user:(const char*)userName:(const char*)password;
  95. - (void)_execCommand:(const char*)cmd user:(const char*)userName:(const char*)password;
  96. - (void)_terminateCommand;
  97. - (void)_killCommand;
  98. @end
  99.  
  100. // -------------------------------------------------------------------------------------
  101. // _ExecServerd definition
  102.  
  103. @interface _ExecServerd : Object <NXSenderIsInvalid, RunService, RootInternal>
  104. { @public
  105.     id                childList;
  106.     char            *serverName;
  107.     id                server;
  108.     int                clientCount;
  109. }
  110. + setExecServerOwner:theOwner;
  111. @end
  112.  
  113. // *************************************************************************************
  114. // *************************************************************************************
  115. // _ExecServerd/RunCommand implementation
  116. // *************************************************************************************
  117. // *************************************************************************************
  118.  
  119. // -------------------------------------------------------------------------------------
  120. // These variables are only set within the child server process
  121. static ExecServer    *exeServerOwner = (id)nil;    // ExecServer instance owner
  122.  
  123. // -------------------------------------------------------------------------------------
  124. // These variables are statically set, and are never changed
  125. static BOOL            _debugMode = NO;            // debug mode
  126.  
  127. // -------------------------------------------------------------------------------------
  128. // static utility functions
  129.  
  130. /* wait for specified child to exit */
  131. static int _waitForExit(int child)
  132. {
  133.     int            pid, omask;
  134.     union wait    status;
  135.     omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
  136.     while (((pid = wait(&status)) != child) && (pid != -1));
  137.     (void)sigsetmask(omask);
  138.     return (status.w_status & 0xFF)? -1 : (status.w_status >> 8) & 0xFF;
  139. }
  140.  
  141. /* check password */
  142. static BOOL _checkPass(const char *pass, const char *pw_passwd)
  143. {
  144.     char    *cp;
  145.     if (!pw_passwd || !*pw_passwd) return NO;
  146.     cp = crypt((char*)pass, (char*)pw_passwd);
  147.     return strcmp(cp, pw_passwd)? NO : YES;
  148. }
  149.  
  150. /* set environment variable */
  151. static int _setenv(char **ep, char *eVal, char *fmt, ...)
  152. {
  153.     va_list            args;
  154.     register char    *cp, *dp;
  155.     va_start(args, fmt);
  156.     vsprintf(eVal, fmt, args);
  157.     va_end(args);
  158.     for (;dp = *ep; ep++) {
  159.         for (cp = eVal; (*cp == *dp) && (*cp != '=') && *cp; cp++, dp++) continue;
  160.         if (((*cp == '=') || !*cp) && ((*dp == '=') || !*dp)) { *ep = eVal; return 0; }
  161.     }
  162.     return -1;
  163. }
  164.  
  165. // -------------------------------------------------------------------------------------
  166. // global user functions
  167.  
  168. /* wrapper for getpwnam() */
  169. struct passwd *exeGetpwnam(const char *user)
  170. {
  171.     extern void        _lu_setport(port_t);
  172.     extern port_t    _lookupd_port(int);
  173.     _lu_setport(_lookupd_port(0));
  174.     return user? getpwnam(user) : getpwuid(getuid());
  175. }
  176.  
  177. /* wrapper for getpwuid() */
  178. struct passwd *exeGetpwuid(uid_t uid)
  179. {
  180.     extern void        _lu_setport(port_t);
  181.     extern port_t    _lookupd_port(int);
  182.     _lu_setport(_lookupd_port(0));
  183.     return getpwuid(uid);
  184. }
  185.  
  186. /* switch to specified user */
  187. int exeSwitchToUser(const char *user, const char *pass)
  188. {
  189.     struct passwd        *pw;
  190.     uid_t                user_uid;
  191.     gid_t                user_gid;
  192.     char                user_name[128], user_passwd[128];
  193.     extern uid_t        exeUserUid; // login user uid
  194.     extern uid_t        exeRootUid; // root user uid
  195.     extern ExecServer    *exeServerOwner; // ExecServer instance owner
  196.  
  197.     /* get user passwd record (cache values) */
  198.     if (!(pw = exeGetpwnam(user))) return RSRV_BADUSER;
  199.     user_uid = pw->pw_uid;
  200.     user_gid = pw->pw_gid;
  201.     strcpy(user_name, pw->pw_name);
  202.     strcpy(user_passwd, pw->pw_passwd);
  203.  
  204.     /* verify password (only in server children) */
  205.     if (exeServerOwner) {
  206.         if (pass) {
  207.             if (!_checkPass(pass, user_passwd)) {
  208.                 struct passwd *rpw = exeGetpwuid(exeRootUid);
  209.                 if (!rpw || !_checkPass(pass, rpw->pw_passwd)) return RSRV_BADPASSWD;
  210.             }
  211.         } else {
  212.             if (user_uid != exeUserUid) return RSRV_BADPASSWD;
  213.         }
  214.     }
  215.  
  216.     /* switch to user */
  217.     if (setgid(user_gid) < 0) return RSRV_BADGID;
  218.     if ((exeRootUid==geteuid()) && initgroups(user_name,user_gid)) return RSRV_BADINIT;
  219.     if (setuid(user_uid) < 0) return RSRV_BADUID;
  220.     
  221.     /* return successful */
  222.     return 0;
  223.     
  224. }
  225.  
  226. // -------------------------------------------------------------------------------------
  227. // shared memory functions
  228.  
  229. /* allocate shared memory (used by Root_process) */
  230. static void *_share_alloc(int size)
  231. {
  232.     kern_return_t    ret;
  233.     vm_address_t    _share_addr;
  234.     ret = vm_allocate(task_self(), &_share_addr, size, 1); 
  235.     if (ret != KERN_SUCCESS) { 
  236.         NXLogError("[_share_alloc] vm_allocate() error %d", ret);
  237.         _exit(1);
  238.     } 
  239.     ret = vm_inherit(task_self(), _share_addr, size, VM_INHERIT_SHARE); 
  240.     if (ret != KERN_SUCCESS) { 
  241.         NXLogError("[_share_alloc] vm_inherit() error %d", ret);
  242.         _exit(1); 
  243.     }
  244.     memset((char*)_share_addr, 0, size);
  245.     return (void*)_share_addr;
  246. }
  247.  
  248. /* free shared memory */
  249. static kern_return_t _share_free(void *_share_mem, int size)
  250. {
  251.     kern_return_t    ret;
  252.     ret = vm_deallocate(task_self(), (vm_address_t)_share_mem, size);
  253.     return ret;
  254. }
  255.  
  256. // -------------------------------------------------------------------------------------
  257. // RunCommand implementation
  258.  
  259. @implementation RunCommand
  260.  
  261. /* initialization */
  262. - init
  263. {
  264.     [super init];
  265.     inputDescriptor    = -1;
  266.     client            = (id)nil;
  267.     killOnError        = YES;
  268.     server            = (id)nil;
  269.     cmdChild        = -1;
  270.     exitErr            = 0;
  271.     shareMem        = (shareStat_t*)_share_alloc(sizeof(shareStat_t));
  272.     return self;
  273. }
  274.  
  275. /* free */
  276. - free
  277. {
  278.     kern_return_t    ret;
  279.     ret = _share_free((void*)shareMem, sizeof(shareStat_t));
  280.     if (ret != KERN_SUCCESS) {
  281.         NXLogError("[RunCommand] vm_deallocate() error %d", ret);
  282.         _exit(1);
  283.     }
  284.     return [super free];
  285. }
  286.  
  287. // -------------------------------------------------------------------------------------
  288.  
  289. /* open pipe to shell and execute command */
  290. - (int)_pipeExec:(const char*)command user:(const char*)userName:(const char*)password
  291. {
  292.     int                    inputP[2], hisOutput, myInput;
  293.     const char            **locEnv = environ;
  294.     extern ExecServer    *exeServerOwner; // ExecServer instance owner
  295.     
  296.     /* open pipe */
  297.     pipe(inputP);
  298.     myInput = inputP[0];
  299.     hisOutput = inputP[1];
  300.     
  301.     /* fork and execute User_process shell */
  302.     if ((cmdChild = forkCHILD) == 0) {
  303.         char **env, *cmd = (char*)command;
  304.         int envi;
  305.         setpgrp(getpid(), getpid());
  306.  
  307.         /* reset pipe */
  308.         close(myInput);
  309.         if (hisOutput != 1) dup2(hisOutput, 1);
  310.         if (hisOutput != 2) dup2(hisOutput, 2);
  311.         if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput);
  312.         
  313.         /* set user */
  314.         if (shareMem->errCode = exeSwitchToUser(userName, password)) {
  315.             sprintf(shareMem->errMsg, "user=%s, pass=%s",
  316.                 (userName?userName:""), (password?password:""));
  317.             _exit(0);
  318.         }
  319.         
  320.         /* make local copy of environment table */
  321.         for (envi = 0; locEnv[envi]; envi++);    // count entries
  322.         env = (char**)alloca(sizeof(char*) * (envi + 3 + 1));    // on stack (extra if needed)
  323.         memset(env, 0, sizeof(char*) * (envi + 3 + 1));
  324.         memcpy(env, locEnv, sizeof(char*) * envi);
  325.         
  326.         /* set path to app */
  327.         if (*(exeServerOwner->mainAppPath)) {
  328.             env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppPath) + 8 + 2);
  329.             sprintf(env[envi], "CMDPATH=%s", exeServerOwner->mainAppPath);
  330.             envi++;
  331.         }
  332.         
  333.         /* set server name */
  334.         if (*(exeServerOwner->mainAppServerName)) {
  335.             env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppServerName) + 11 + 2);
  336.             sprintf(env[envi], "SERVERNAME=%s", exeServerOwner->mainAppServerName);
  337.             envi++;
  338.             if (*(exeServerOwner->mainAppHost)) {
  339.                 env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppHost) + 11 + 2);
  340.                 sprintf(env[envi], "SERVERHOST=%s", exeServerOwner->mainAppHost);
  341.                 envi++;
  342.             }
  343.         }
  344.         
  345.         /* update environment if user change */
  346.         if (userName && *userName) {
  347.             struct passwd *upw = exeGetpwnam(userName);
  348.             _setenv(env,(char*)alloca(strlen(upw->pw_dir)+7),"HOME=%s",upw->pw_dir);
  349.             _setenv(env,(char*)alloca(strlen(userName)+7),"USER=%s",userName);
  350.         }
  351.  
  352.         /* check for script file wrapper */
  353.         memset(shareMem->execScript, 0, sizeof(shareMem->execScript));
  354.         if (cmd && !strncmp(cmd, "#!", 2)) {
  355.             FILE *fhnd;
  356.             sprintf(shareMem->execScript, "/tmp/.execScript_%d", getpid());
  357.             unlink(shareMem->execScript);
  358.             fhnd = fopen(shareMem->execScript, "w");
  359.             if (fhnd) {
  360.                 fputs(cmd, fhnd);
  361.                 fputs("\n", fhnd);
  362.                 fchmod(fileno(fhnd), 0755);
  363.                 fclose(fhnd);
  364.                 cmd = shareMem->execScript;
  365.             }
  366.         }
  367.         
  368.         /* execute command */
  369.         shareMem->errCode = RSRV_SUCCESS;    // make sure it's been reset
  370.         execle("/bin/csh", "csh", "-f", "-c", cmd, (char*)nil, env);
  371.         shareMem->errCode = RSRV_EXEC;
  372.         _exit(0);
  373.         
  374.     }
  375.     
  376.     /* set io */
  377.     if (cmdChild == -1)  { close(myInput); myInput = -1; }
  378.     close(hisOutput);
  379.     
  380.     return myInput;
  381. }
  382.  
  383. /* thread to wait for command completion */
  384. - (void*)_waitForComplete
  385. {
  386.  
  387.     /* read data if inputDescriptor is valid */
  388.     if (inputDescriptor >= 0) {
  389.         int cnt;
  390.         char buffer[1025];
  391.         do {
  392.             cnt = read(inputDescriptor, buffer, 1024);
  393.             if (cnt != -1) [server _commandOutput:buffer len:cnt execId:(u_int)self];
  394.         } while (cnt > 0);
  395.         if ((exitErr=_waitForExit(cmdChild)) < 0) shareMem->errCode = RSRV_ABORTED;
  396.         inputDescriptor = cmdChild = -1;    // indicate that child is now inactive
  397.     } else {
  398.         shareMem->errCode = RSRV_FORK;    // command never even had a chance
  399.     }
  400.     
  401.     /* indicate command completion */
  402.     [server _commandDidComplete:(u_int)self];
  403.  
  404.     return self;
  405.     
  406. }
  407.  
  408. /* thread router */
  409. static void *_waitForComplete(id fakeSelf)
  410. {
  411.     return (void*)[(RunCommand*)fakeSelf _waitForComplete];
  412. }
  413.  
  414. /* execute command */
  415. - (void)_execCommand:(const char*)cmd user:(const char*)user:(const char*)pass
  416. {
  417.     inputDescriptor = [self _pipeExec:cmd user:user:pass];
  418.     cthread_detach(cthread_fork((cthread_fn_t)_waitForComplete,self));
  419. }
  420.  
  421. /* terminate command (MAY be blocked) */
  422. - (void)_terminateCommand
  423. {
  424.     if (cmdChild > 0) {
  425.         killpg(cmdChild, SIGTERM);
  426.         kill(cmdChild, SIGTERM);
  427.     }
  428. }
  429.  
  430. /* kill command (CANNOT be blocked) */
  431. - (void)_killCommand
  432. {
  433.     if (cmdChild > 0) {
  434.         killpg(cmdChild, SIGKILL);
  435.         kill(cmdChild, SIGKILL);
  436.     }
  437. }
  438.  
  439. // -------------------------------------------------------------------------------------
  440. // RunCommand: sender(client) connection is invalid
  441. - senderIsInvalid:sender
  442. {
  443.     NXLogError("[RunCommand] Connection to client failed ...");
  444.     client = (id)nil;    // clear client
  445.     if (killOnError) [self _killCommand];
  446.     return self;
  447. }
  448.  
  449. @end
  450.  
  451. // -------------------------------------------------------------------------------------
  452. // _ExecServerd implementation
  453.  
  454. #define isMyCHILD(X)    ((X) && ([childList indexOf:(id)(X)] != NX_NOT_IN_LIST))
  455.  
  456. @implementation _ExecServerd
  457.  
  458. /* set exeServerOwner. (this is set in the child process only) */
  459. + setExecServerOwner:theOwner
  460. {
  461.     extern ExecServer    *exeServerOwner; // ExecServer instance owner
  462.     exeServerOwner = theOwner;
  463.     return self;
  464. }
  465.  
  466. /* init instance */
  467. - init
  468. {
  469.     [super init];
  470.     childList = [[[List alloc] initCount:1] empty];
  471.     serverName = (char*)nil;
  472.     server = (id)nil;
  473.     clientCount = 0;
  474.     return self;
  475. }
  476.  
  477. // -------------------------------------------------------------------------------------
  478. // Root_process commands
  479. // -------------------------------------------------------------------------------------
  480.  
  481. /* remote: client initiated connection to server */
  482. - (void)_pingServer
  483. {
  484.  
  485.     /* count active client */
  486.     clientCount++;
  487.     
  488. }
  489.  
  490. /* remote: return 'root' flag */
  491. - (BOOL)_isRunningAsRoot
  492. {
  493.     return (exeGetpwnam("root")->pw_uid == geteuid())? YES : NO;
  494. }
  495.  
  496. /* remote: exec command */
  497. - (execHandle_t)_runCommand:(const char*)command
  498.         user:(const char*)userName:(const char*)password
  499.         client:(id <RemoteClient>)theClient
  500.         kill:(BOOL)killOnError
  501. {
  502.     RunCommand            *runCmd;
  503.     
  504.     /* create/initialize command executor */
  505.     runCmd = [[RunCommand alloc] init];
  506.     runCmd->killOnError = killOnError;
  507.     runCmd->server = server;
  508.     if (theClient) {
  509.         NXConnection *conn = [(NXProxy*)theClient connectionForProxy];
  510.         runCmd->client = theClient;
  511.         [(NXProxy*)theClient setProtocolForProxy:@protocol(RemoteClient)];
  512.         [[conn outPort] registerForInvalidationNotification:runCmd];
  513.     }
  514.  
  515.     /* keep track of all children */
  516.     [childList addObject:runCmd];
  517.     
  518.     /* set command to execute */
  519.     [runCmd _execCommand:command user:userName:password];
  520.     
  521.     /* free arguments */
  522.     if (command) free((char*)command);
  523.     if (userName) free((char*)userName);
  524.     if (password) free((char*)password);
  525.     
  526.     return (execHandle_t)runCmd;
  527. }
  528.  
  529. /* remote: open pipe to shell and execute method */
  530. - (int)_uperform:(SEL)method withArg:(const char*)arg
  531.         user:(const char*)userName:(const char*)password
  532. {
  533.     int                    child, exitErr;
  534.     shareStat_t            *share;
  535.     extern ExecServer    *exeServerOwner; // ExecServer instance owner
  536.  
  537.     /* allocate storage shared with child process */
  538.     share = (shareStat_t*)_share_alloc(sizeof(shareStat_t));
  539.     
  540.     /* single pass loop */
  541.     for (;;) {
  542.         id ms = exeServerOwner->methodDelegate;
  543.     
  544.         /* check for methServer response to method */
  545.         if (!ms || ![ms respondsTo:method]) {
  546.             exitErr = RSRV_UNDEF;
  547.             break;
  548.         }
  549.     
  550.         /* fork and perform method */
  551.         if ((child = forkCHILD) == 0) {
  552.             setpgrp(getpid(), getpid());
  553.             if (share->errCode = exeSwitchToUser(userName, password)) {
  554.                 sprintf(share->errMsg, "user=%s, pass=%s", userName, password);
  555.                 _exit(0);
  556.             }
  557.             _exit((int)[ms perform:method with:(id)arg] & 0xFF);
  558.         }
  559.  
  560.         /* check for fork failure */
  561.         if (child == -1) {
  562.             exitErr =  RSRV_FORK;
  563.             break;
  564.         }
  565.     
  566.         /* wait for child to exit */
  567.         exitErr = _waitForExit(child);
  568.         if (share->errCode) exitErr = share->errCode;
  569.         break;
  570.         
  571.     }
  572.     
  573.     /* free arguments and return */
  574.     _share_free((void*)share, sizeof(shareStat_t));
  575.     if (arg) free((char*)arg);
  576.     if (userName) free((char*)userName);
  577.     if (password) free((char*)password);
  578.     return exitErr;
  579.  
  580. }
  581.  
  582. /* terminate command */
  583. - (void)_terminateCommand:(execHandle_t)runId
  584. {
  585.     RunCommand    *runCmd = (RunCommand*)runId;
  586.     if (isMyCHILD(runCmd)) [runCmd _terminateCommand];
  587. }
  588.  
  589. /* kill command */
  590. - (void)_killCommand:(execHandle_t)runId
  591. {
  592.     RunCommand    *runCmd = (RunCommand*)runId;
  593.     if (isMyCHILD(runCmd)) [runCmd _killCommand];
  594. }
  595.  
  596. /* return true if child is still active */
  597. - (BOOL)_childIsActive:(execHandle_t)runId
  598. {
  599.     RunCommand    *runCmd = (RunCommand*)runId;
  600.     return (isMyCHILD(runCmd))? YES : NO;
  601. }
  602.  
  603. /* shut down server */
  604. - (void)_shutDownServer
  605. {
  606.     int                    i;
  607.     extern ExecServer    *exeServerOwner; // ExecServer instance owner
  608.     exeServerOwner->exitWhenDone = YES;
  609.     for (i = 0; i < [childList count]; i++) {
  610.         RunCommand    *runId = (RunCommand*)[childList objectAt:i];
  611.         [runId _killCommand];
  612.     }
  613. }
  614.  
  615. // -------------------------------------------------------------------------------------
  616. // internal Root_process notification
  617. // -------------------------------------------------------------------------------------
  618.  
  619. - (void)_commandOutput:(const char*)buff len:(int)len execId:(u_int)fakeRun
  620. {
  621.     RunCommand    *runCmd = (RunCommand*)fakeRun;
  622.     
  623.     /* send text output to client */
  624.     if (runCmd->client) [runCmd->client commandOutput:buff len:len];
  625.     
  626.     /* free buffer */
  627.     free((char*)buff);
  628.     
  629. }
  630.  
  631. - (void)_commandDidComplete:(u_int)fakeRun
  632. {
  633.     RunCommand            *runCmd = (RunCommand*)fakeRun;
  634.     int                    err;
  635.     extern BOOL            _debugMode; // debug mode
  636.     extern ExecServer    *exeServerOwner; // ExecServer instance owner
  637.     
  638.     /* debug message */
  639.     if (_debugMode && *(runCmd->shareMem->errMsg))    {    // debuging purposes
  640.         NXLogError("[_ExecServerd] (0x%X) %s",
  641.             runCmd->shareMem->errCode, runCmd->shareMem->errMsg);
  642.     }
  643.         
  644.     /* message client */
  645.     err = runCmd->shareMem->errCode?runCmd->shareMem->errCode:runCmd->exitErr;
  646.     if (runCmd->client) [runCmd->client commandDidCompleteWithError:err];
  647.     
  648.     /* unregister runCmd */
  649.     if (runCmd->client) {
  650.         NXPort *port = [[(NXProxy*)runCmd->client connectionForProxy] outPort];
  651.         [port unregisterForInvalidationNotification:runCmd];
  652.     }
  653.     [NXConnection unregisterForInvalidationNotification:runCmd];
  654.  
  655.     /* remove script wrapper, if any */
  656.     if (*(runCmd->shareMem->execScript)) unlink(runCmd->shareMem->execScript);
  657.     
  658.     /* free resources */
  659.     [NXConnection removeObject:runCmd];
  660.     [childList removeObject:runCmd];
  661.     [runCmd free];
  662.     
  663.     /* check for exit */
  664.     if (!clientCount && ([childList count] <= 0) && exeServerOwner->exitWhenDone) {
  665.         NXLogError("[_ExecServerd] Server terminating...");
  666.         _exit(0);
  667.     }
  668.     
  669. }
  670.  
  671. // -------------------------------------------------------------------------------------
  672. // external notification
  673. // -------------------------------------------------------------------------------------
  674.  
  675. /* from connection startup notification */
  676. - connection:(NXConnection*)conn didConnect:(NXConnection*)newConn
  677. {
  678.     [[newConn outPort] registerForInvalidationNotification:self];
  679.     return newConn;
  680. }
  681.  
  682. /* server: mainApp(client) connection went bad (client died?) */
  683. - senderIsInvalid:sender
  684. {
  685.  
  686.     /* decrement client count */
  687.     clientCount--;
  688.     
  689.     /* terminate now if no children */
  690.     if (!clientCount && ([childList count] <= 0)) {
  691.         NXLogError("[_ExecServerd] Server terminating...");
  692.         _exit(0);
  693.     }
  694.     
  695.     return self;
  696.     
  697. }
  698.  
  699. @end
  700.  
  701. // *************************************************************************************
  702. // *************************************************************************************
  703. // ExecServer implementation
  704. // *************************************************************************************
  705. // *************************************************************************************
  706.  
  707. /* print already running error */
  708. #define IS_RUNNING    (isRunning? _alreadyRunning(self,_cmd) : 0) 
  709. static int _alreadyRunning(id self, SEL _cmd)
  710. {
  711.     NXLogError("[ExecServer] Attempt to set attribute after server has started");
  712.     return 1;
  713. }
  714.  
  715. @implementation ExecServer
  716.  
  717. // -------------------------------------------------------------------------------------
  718. // ExecServer class methods
  719. // -------------------------------------------------------------------------------------
  720.  
  721. /* return error code description */
  722. typedef struct {
  723.     int                    err;
  724.     char                *desc;
  725. } _shellError_t;
  726. static _shellError_t    _shellErrors[] = {
  727.     { RSRV_ABORTED,        "Command Aborted" },
  728.     { RSRV_BADPASSWD,    "Invalid User Password" },
  729.     { RSRV_BADUSER,        "Invalid User Name" },
  730.     { RSRV_BADGID,        "setgid() Error: Permission Denied" },
  731.     { RSRV_BADUID,        "setuid() Error: Permission Denied" },
  732.     { RSRV_BADINIT,        "initgroups() Error: Permission Denied" },
  733.     { RSRV_EXEC,        "Unable to Execute Command" },
  734.     { RSRV_RSH,            "Remote rsh exec() failed" },
  735.     { RSRV_FORK,        "Unable to Create New Process" },
  736.     { RSRV_UNKNOWN,        "Unknown Error" },
  737.     { RSRV_UNDEF,        "Undefined target/process" },
  738.     { RSRV_SUCCESS,        "Command Completed Successfully" },    // must be last
  739.     { 0,                 (char*)nil }
  740. };
  741. static char *_errorDesc(int err)
  742. {
  743.     _shellError_t    *e;
  744.     for (e = _shellErrors; e->desc && (err != e->err); e++);
  745.     return e->desc;
  746. }
  747. + (char*)errorDesc:(int)err
  748. {
  749.     return _errorDesc(err);
  750. }
  751.  
  752. /* cache uids */
  753. + (void)_cacheUid
  754. {
  755.     struct passwd            *pw;
  756.     extern uid_t            exeUserUid; // login user uid
  757.     extern uid_t            exeRootUid; // root user uid
  758.     extern struct passwd    *exeGetpwnam(const char *user);
  759.     
  760.     /* get 'root' uid */
  761.     if (!(pw = exeGetpwnam("root"))) {
  762.         NXLogError("[ExecServer] exeGetpwnam(\"root\") failed");
  763.         exit(1);
  764.     }
  765.     exeRootUid = pw->pw_uid;
  766.  
  767.     /* get login uid */
  768.     if (!(pw = exeGetpwnam((char*)nil))) {
  769.         NXLogError("[ExecServer] Unknown login user name");
  770.         exit(1);
  771.     }
  772.     exeUserUid = pw->pw_uid;
  773.     
  774. }
  775.  
  776. // -------------------------------------------------------------------------------------
  777. // ExecServer initialization
  778. // -------------------------------------------------------------------------------------
  779.  
  780. /* ExecServer initialization */
  781. - init
  782. {
  783.     char    localHostName[MAXHOSTNAMELEN + 1];
  784.     [super init];
  785.     isRunning = NO;
  786.     exitWhenDone = YES;
  787.     rootServer = (id)nil;
  788.     methodDelegate = (id)nil;
  789.     memset(mainAppPath, 0, sizeof(mainAppPath));
  790.     memset(mainAppHost, 0, sizeof(mainAppHost));
  791.     memset(mainAppServerName, 0, sizeof(mainAppServerName));
  792.     memset(remoteHost, 0, sizeof(remoteHost));
  793.     gethostname(localHostName, sizeof(localHostName));
  794.     sprintf(remoteServerName, "ExecServer_%s_%d", localHostName, getpid());
  795.     memset(serverCommandName, 0, sizeof(serverCommandName));
  796.     return self;
  797. }
  798.  
  799. - free
  800. {
  801.     // not yet implemented
  802.     return [super free];
  803. }
  804.  
  805. // -------------------------------------------------------------------------------------
  806. // ExecServer attributes
  807. // -------------------------------------------------------------------------------------
  808.  
  809. /* set exit when done flag (NOTE: MUST be called BEFORE startServer!) */
  810. - setExitWhenDone:(BOOL)flag
  811. {
  812.     exitWhenDone = flag;
  813.     return self;
  814. }
  815.  
  816. /* set method delegate class (NOTE: MUST be called BEFORE startServer!) */
  817. - setMethodDelegate:classId
  818. {
  819.     if (IS_RUNNING) return (id)nil;
  820.     methodDelegate = (classId == [classId class])? classId : (id)nil;
  821.     return methodDelegate;
  822. }
  823.  
  824. /* set Main app path */
  825. - setMainAppPath:(const char*)appPath
  826. {
  827.     if (IS_RUNNING) return (id)nil;
  828.     if (appPath) strcpy(mainAppPath, appPath);
  829.     else *mainAppPath = 0;
  830.     return self;
  831. }
  832.  
  833. /* set remote server name used in environment (NOTE: MUST be called BEFORE startServer!) */
  834. - setMainAppServerName:(const char*)servName host:(const char*)hostName
  835. {
  836.     if (IS_RUNNING) return (id)nil;
  837.     if (!servName || !*servName) *mainAppServerName = 0;
  838.     else {
  839.         strncpy(mainAppServerName, servName, sizeof(mainAppServerName));
  840.         if (!hostName || !*hostName) *mainAppHost = 0;
  841.         else strncpy(mainAppHost, hostName, sizeof(mainAppHost));
  842.     }
  843.     return self;
  844. }
  845.  
  846. /* return main app server name */
  847. - (const char*)mainAppServerName
  848. {
  849.     return mainAppServerName;
  850. }
  851.  
  852. /* return main app host name */
  853. - (const char*)mainAppHost
  854. {
  855.     return mainAppHost;
  856. }
  857.  
  858. /* set remote host name (NOTE: MUST be called BEFORE startServer!) */
  859. - setRemoteHost:(const char*)hostName
  860. {
  861.     if (IS_RUNNING) return (id)nil;
  862.     if (!hostName) *remoteHost = 0;
  863.     else strncpy(remoteHost, hostName, sizeof(remoteHost));
  864.     return self;
  865. }
  866.  
  867. /* return remote server name */
  868. - (const char*)remoteHost
  869. {
  870.     return remoteHost;
  871. }
  872.  
  873. /* set name of remote ExecServer (NOTE: MUST be called BEFORE startServer!) */
  874. - setRemoteServerName:(const char*)serverName
  875. {
  876.     if (IS_RUNNING) return (id)nil;
  877.     strncpy(remoteServerName, serverName, MAXHOSTNAMELEN);
  878.     return self;
  879. }
  880.  
  881. /* return remote runServer name */
  882. - (const char*)remoteServerName
  883. {
  884.     return remoteServerName;
  885. }
  886.  
  887. /* set RemoteExecServer command path/name (NOTE: MUST be called BEFORE startServer!) */
  888. - setServerCommandName:(const char*)cmdPath
  889. {
  890.     if (IS_RUNNING) return (id)nil;
  891.     if (!cmdPath) *serverCommandName = 0;
  892.     else {
  893.         struct stat    st;
  894.         if (stat((char*)cmdPath,&st)) return (id)nil;
  895.         strncpy(serverCommandName, cmdPath, sizeof(serverCommandName));
  896.     }
  897.     return self;
  898. }
  899.  
  900. // -------------------------------------------------------------------------------------
  901. // ExecServer startup
  902.  
  903. /* ExecServer mainline (does not return) */
  904. - (void)_runServer
  905. {
  906.     _ExecServerd            *rootId;
  907.     NXConnection            *connection;
  908.     extern uid_t            exeRootUid; // root user uid
  909.     extern struct passwd    *exeGetpwuid(uid_t uid);
  910.  
  911.     /* (re)cache uids */
  912.     [_ExecServerd setExecServerOwner:self];
  913.     [ExecServer _cacheUid];
  914.         
  915.     /* set process group */
  916.     setpgrp(getpid(), getpid());
  917.         
  918.     /* init root process */
  919.     if (exeRootUid == geteuid()) {
  920.         int err = 0;
  921.         struct passwd *pw;
  922.         if (!(pw = exeGetpwuid(exeRootUid))) err = RSRV_BADUSER;
  923.         else if (setgid(pw->pw_gid) < 0) err = RSRV_BADGID;
  924.         else if (initgroups(pw->pw_name, pw->pw_gid)) err = RSRV_BADINIT;
  925.         else if (setuid(exeRootUid) < 0) err = RSRV_BADUID;
  926.         if (err) {
  927.             NXLogError("[ExecServer] Server User 'root': %s", _errorDesc(err));
  928.             _exit(1);
  929.         }
  930.     }
  931.         
  932.     /* start server */
  933. //    NXLogError("[ExecServer] starting %s", remoteServerName);
  934.     rootId = [[_ExecServerd alloc] init];
  935.     connection = [NXConnection registerRoot:rootId withName:remoteServerName];
  936.     rootId->serverName = STRCOPY(remoteServerName);
  937.     rootId->server = (id)[NXConnection connectToPort:[connection inPort]];
  938.     [(NXProxy*)rootId->server setProtocolForProxy:@protocol(RootInternal)];
  939.     [NXPort worryAboutPortInvalidation];
  940.     [connection registerForInvalidationNotification:rootId];
  941.     [connection setDelegate:rootId];
  942.     [connection run];
  943.     _exit(0);
  944.  
  945. }
  946.  
  947. /* connect to server */
  948. - _connectToServer:(int)tries
  949. {
  950.     
  951.     /* establish connection to server, retry until successful */
  952.     if (!rootServer) {
  953.         int i;
  954.         for (i = tries;;) {
  955.             rootServer = (_ExecServerd*)[NXConnection connectToName:remoteServerName
  956.                 onHost:(*remoteHost?remoteHost:(char*)nil)];
  957.             if (rootServer || (--i <= 0)) break;
  958.             sleep(1);
  959.         }
  960.     }
  961.     
  962.     /* return connection */
  963.     return (_ExecServerd*)rootServer;
  964.     
  965. }
  966.  
  967. /* start root server (MUST be called only ONCE at the beginning of the application) */
  968. - startServer
  969. {
  970.     NXConnection    *connection;
  971.     int                err;
  972.     extern int        exeSwitchToUser(const char *user, const char *pass);
  973.  
  974.     /* check for already running */
  975.     if (isRunning) return self;
  976.  
  977.     /* set timeout defaults */
  978.     [NXConnection setDefaultTimeout:-1];
  979.  
  980.     /* (re)cache uids */
  981.     [ExecServer _cacheUid];
  982.  
  983.     /* start up server */
  984.     if (*remoteHost) {
  985.     
  986.         if (![self _connectToServer:1]) {
  987.         
  988.             /* start ExecServer process */
  989.             if ((rootChild = forkCHILD) == 0) {
  990.                 char cmd[2048], *c = cmd;
  991.                 setpgrp(getpid(), getpid());
  992.                 sprintf(c, "%s -r %s", serverCommandName, remoteServerName);
  993.                 c += strlen(c);
  994.                 if (exitWhenDone) {
  995.                     sprintf(c, " -e");
  996.                     c += strlen(c);
  997.                 }
  998.                 if (*mainAppPath) {
  999.                     sprintf(c, " -c %s", mainAppPath);
  1000.                     c += strlen(c);
  1001.                 }
  1002.                 if (*mainAppServerName) {
  1003.                     sprintf(c, " -m %s", mainAppServerName);
  1004.                     c += strlen(c);
  1005.                     if (*mainAppHost) {
  1006.                         sprintf(c, " %s", mainAppHost);
  1007.                         c += strlen(c);
  1008.                     }
  1009.                 }
  1010.                 execl("/usr/ucb/rsh", "/usr/ucb/rsh", remoteHost, "-n", cmd, NULL);
  1011.                 NXLogError("[ExecServer] %s", _errorDesc(RSRV_RSH));
  1012.                 exit(1);    // child process termination error
  1013.             }
  1014.     
  1015.             /* connect to server and return */
  1016.             if (![self _connectToServer:12]) {
  1017.                 NXLogError("[ExecServer] Cannot connect to server '%s' on '%s'",
  1018.                     remoteServerName, remoteHost);
  1019.                 killpg(rootChild, SIGKILL);
  1020.                 kill(rootChild, SIGKILL);
  1021.                 return (id)nil;
  1022.             }
  1023.             
  1024.         } else {
  1025.         
  1026.             NXLogError("[ExecServer] Connected to existing server '%s' on '%s'",        
  1027.                     remoteServerName, remoteHost);
  1028.  
  1029.         }
  1030.     
  1031.     } else {
  1032.     
  1033.         /* start ExecServer process */
  1034.         if ((rootChild = forkCHILD) == 0) {
  1035.             setpgrp(getpid(), getpid());
  1036.             [self _runServer];
  1037.             exit(1);    // child process termination error
  1038.         }
  1039.     
  1040.         /* connect to server and return */
  1041.         if (![self _connectToServer:7]) {
  1042.             NXLogError("[ExecServer] Cannot connect to local server '%s'", remoteServerName);
  1043.             killpg(rootChild, SIGKILL);
  1044.             kill(rootChild, SIGKILL);
  1045.             return (id)nil;
  1046.         }
  1047.     
  1048.     }
  1049.     
  1050.     /* connection successful */
  1051.     isRunning = YES;
  1052.     
  1053.     /* switch to normal login user (password not required) */
  1054.     if (err = exeSwitchToUser((char*)nil, (char*)nil)) {
  1055.         NXLogError("[ExecServer] login user: %s", _errorDesc(err));
  1056.         return (id)nil;
  1057.     }
  1058.  
  1059.     /* initialize server connection */
  1060.     connection = [(NXProxy*)rootServer connectionForProxy];
  1061.     [(NXProxy*)rootServer setProtocolForProxy:@protocol(RunService)];
  1062.     [connection registerForInvalidationNotification:self];
  1063.     [connection runFromAppKit];
  1064.     [(_ExecServerd*)rootServer _pingServer];
  1065.     
  1066.     /* return successful */
  1067.     return self;
  1068.     
  1069. }
  1070.  
  1071. // -------------------------------------------------------------------------------------
  1072. // password check
  1073. // -------------------------------------------------------------------------------------
  1074.  
  1075. /* check user password flag */
  1076. - (BOOL)needUserPassword:(const char*)user
  1077. {
  1078.     struct passwd            *pw;
  1079.     extern uid_t            exeUserUid; // login user uid
  1080.     extern struct passwd    *exeGetpwnam(const char *user);
  1081.     if (!(pw = exeGetpwnam(user))) return YES;    // invalid user
  1082.     if (pw->pw_uid == exeUserUid) return NO;
  1083.     return YES;
  1084. }
  1085.  
  1086. // -------------------------------------------------------------------------------------
  1087. // ExecServer services
  1088. // -------------------------------------------------------------------------------------
  1089.  
  1090. - (BOOL)isRunningAsRoot
  1091. {
  1092.     BOOL    isRoot;
  1093.     if (!rootServer) return NO;
  1094. NX_DURING
  1095.     isRoot = [(_ExecServerd*)rootServer _isRunningAsRoot];
  1096. NX_HANDLER
  1097.     isRoot = NO;
  1098. NX_ENDHANDLER
  1099.     return isRoot;
  1100. }
  1101.  
  1102. /* issue run command */
  1103. - (execHandle_t)runCommand:(const char*)cmd
  1104.         withUser:(const char*)userName:(const char*)password
  1105.         forClient:(id <RemoteClient>)client
  1106.         killOnError:(BOOL)killOnError
  1107. {
  1108.     execHandle_t    runId;
  1109.     if (!rootServer) return (execHandle_t)nil;
  1110. NX_DURING
  1111.     runId = [(_ExecServerd*)rootServer _runCommand:cmd user:userName:password
  1112.                 client:client kill:killOnError];
  1113. NX_HANDLER
  1114.     runId = (execHandle_t)nil;
  1115. NX_ENDHANDLER
  1116.     return runId;
  1117. }
  1118.  
  1119. /* issue run command */
  1120. - (execHandle_t)runCommand:(const char*)cmd
  1121.         forClient:(id <RemoteClient>)client
  1122.         killOnError:(BOOL)killOnError
  1123. {
  1124.     return [self runCommand:cmd withUser:(char*)nil:(char*)nil
  1125.         forClient:client killOnError:killOnError];
  1126. }
  1127.  
  1128. /* terminate command */
  1129. - terminateCommand:(execHandle_t)runId
  1130. {
  1131.     id    rtn = self;
  1132.     if (!rootServer) return (id)nil;
  1133. NX_DURING    
  1134.     [(_ExecServerd*)rootServer _terminateCommand:(execHandle_t)runId];
  1135. NX_HANDLER
  1136.     rtn = (id)nil;
  1137. NX_ENDHANDLER       
  1138.     return rtn;
  1139. }
  1140.  
  1141. /* kill command (unmaskable) */
  1142. - killCommand:(execHandle_t)runId
  1143. {
  1144.     id    rtn = self;
  1145.     if (!rootServer) return (id)nil;
  1146. NX_DURING    
  1147.     [(_ExecServerd*)rootServer _killCommand:(execHandle_t)runId];
  1148. NX_HANDLER
  1149.     rtn = (id)nil;
  1150. NX_ENDHANDLER       
  1151.     return rtn;
  1152. }
  1153.  
  1154. /* returns true if command handle is still valid */
  1155. - (BOOL)commandIsActive:(execHandle_t)runId
  1156. {
  1157.     BOOL    rtn = NO;
  1158.     if (!rootServer) return NO;
  1159. NX_DURING    
  1160.     rtn = [(_ExecServerd*)rootServer _childIsActive:(execHandle_t)runId];
  1161. NX_HANDLER
  1162.     rtn = NO;
  1163. NX_ENDHANDLER
  1164.     return rtn;
  1165. }
  1166.  
  1167. /* shut down _ExecServerd */
  1168. - shutDownServer
  1169. {
  1170.     id    rtn = self;
  1171.     if (!rootServer) return self;
  1172. NX_DURING    
  1173.     [(_ExecServerd*)rootServer _shutDownServer];
  1174. NX_HANDLER
  1175.     rtn = (id)nil;
  1176. NX_ENDHANDLER
  1177.     return rtn;
  1178. }
  1179.  
  1180. // -------------------------------------------------------------------------------------
  1181.  
  1182. /* perform method from server */
  1183. - (int)perform:(SEL)method withArg:(const char*)arg
  1184.         withUser:(const char*)userName:(const char*)password;
  1185. {
  1186.     int            rtn;
  1187.     if (!rootServer) return RSRV_UNDEF;
  1188. NX_DURING
  1189.     rtn = (int)[(_ExecServerd*)rootServer _uperform:method withArg:arg
  1190.             user:userName:password];
  1191. NX_HANDLER
  1192.     rtn = RSRV_UNKNOWN;
  1193. NX_ENDHANDLER
  1194.     return rtn;
  1195. }
  1196.  
  1197. // -------------------------------------------------------------------------------------
  1198. // failure notification
  1199. // -------------------------------------------------------------------------------------
  1200.  
  1201. /* mainApp: connection to server went bad (server died?) */
  1202. - senderIsInvalid:sender
  1203. {
  1204.     rootServer = (id)nil;    // ??????
  1205.     NXLogError("[ExecServer] Server port went bad!");
  1206.     _exit(1);
  1207.     return self;
  1208. }
  1209.  
  1210. @end
  1211.